// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//

#include <cantor/parser/ast/clause.h>
#include <cantor/parser/ast/stmt.h>

namespace cantor::parser {
    nlohmann::ordered_json WhenClauseCase::to_json() const {
        auto j = DefiniteExpr::to_json_impl();
        if (when_expr) {
            j["when_expr"] = when_expr->to_json();
        } else {
            j["when_expr"] = nlohmann::ordered_json();
        }
        if (then_expr) {
            j["then_expr"] = then_expr->to_json();
        } else {
            j["then_expr"] = nlohmann::ordered_json();
        }
        return j;
    }

    bool WhenClauseCase::is_complex_node() {
        if (when_expr && when_expr->is_complex_node()) {
            return true;
        }
        if (then_expr && then_expr->is_complex_node()) {
            return true;
        }
        return false;
    }

    nlohmann::ordered_json WhenClause::to_json() const {
        auto j = DefiniteExpr::to_json_impl();
        if (case_expr) {
            j["case_expr"] = case_expr->to_json();
        } else {
            j["case_expr"] = nlohmann::ordered_json();
        }
        auto ws = nlohmann::ordered_json::array();
        for (auto i = 0; i < when_clauses.size(); i++) {
            ws.push_back(when_clauses[i]->to_json());
        }
        j["when_clauses"] = ws;
        if (else_expr) {
            j["else_expr"] = else_expr->to_json();
        } else {
            j["else_expr"] = nlohmann::ordered_json();
        }
        return j;
    }

    bool WhenClause::is_complex_node() {
        if (case_expr && case_expr->is_complex_node()) {
            return true;
        }
        for (auto i = 0; i < when_clauses.size(); i++) {
            if (when_clauses[i]->is_complex_node()) {
                return true;
            }
        }
        if (else_expr && else_expr->is_complex_node()) {
            return true;
        }
        return false;
    }

    nlohmann::ordered_json NamedType::to_json() const {
        auto j = DefiniteExpr::to_json_impl();
        j["name"] = name.to_string();
        if (type) {
            j["type"] = type->to_json();
        } else {
            j["type"] = nlohmann::ordered_json();
        }
        return j;
    }

    nlohmann::ordered_json ConstructArg::to_json_impl() const {
        auto j = DefiniteExpr::to_json_impl();
        return j;
    }

    nlohmann::ordered_json MapPair::to_json() const {
        auto j = ConstructArg::to_json_impl();
        if (key) {
            j["key"] = key->to_json();
        } else {
            j["key"] = nlohmann::ordered_json();
        }
        if (value) {
            j["value"] = value->to_json();
        } else {
            j["value"] = nlohmann::ordered_json();
        }
        return j;
    }

    nlohmann::ordered_json DictPair::to_json() const {
        auto j = ConstructArg::to_json_impl();
        j["arg_name"] = name.to_string();
        if (value) {
            j["value"] = value->to_json();
        } else {
            j["value"] = nlohmann::ordered_json();
        }
        return j;
    }

    nlohmann::ordered_json MapArg::to_json() const {
        auto j = ConstructArg::to_json_impl();
        auto ps = nlohmann::ordered_json::array();
        for (int i = 0; i < values.size(); i++) {
            ps.push_back(values[i]->to_json());
        }
        j["values"] = ps;
        return j;
    }

    nlohmann::ordered_json DictArg::to_json() const {
        auto j = ConstructArg::to_json_impl();
        auto ps = nlohmann::ordered_json::array();
        for (int i = 0; i < values.size(); i++) {
            ps.push_back(values[i]->to_json());
        }
        j["values"] = ps;
        return j;
    }

    nlohmann::ordered_json ExprListArg::to_json() const {
        auto j = ConstructArg::to_json_impl();
        auto ps = nlohmann::ordered_json::array();
        for (int i = 0; i < values.size(); i++) {
            ps.push_back(values[i]->to_json());
        }
        j["values"] = ps;
        return j;
    }

    nlohmann::ordered_json EnumArg::to_json() const {
        auto j = ConstructArg::to_json_impl();
        if (uri) {
            j["uri"] = uri->to_json();
        } else {
            j["uri"] = nlohmann::ordered_json();
        }
        j["is_field"] = is_field;
        j["field"] = field.to_string();
        j["value"] = value;
        return j;
    }

    nlohmann::ordered_json ProtoArg::to_json() const {
        auto j = ConstructArg::to_json_impl();
        j["uri"] = uri.to_string();
        auto ps = nlohmann::ordered_json::array();
        for (int i = 0; i < values.size(); i++) {
            ps.push_back(values[i]->to_json());
        }
        j["values"] = ps;
        return j;
    }

    nlohmann::ordered_json UdfArg::to_json() const {
        auto j = ConstructArg::to_json_impl();
        if (uri) {
            j["uri"] = uri->to_json();
        } else {
            j["uri"] = nlohmann::ordered_json();
        }
        if (arg) {
            switch (arg->expr_type) {
                case cantor::pb::ast::ENT_MAP_ARG:
                    j["arg"] = ((const MapArg*)arg)->to_json();
                    break;
                case cantor::pb::ast::ENT_DICT_ARG:
                    j["arg"] = ((const DictArg*)arg)->to_json();
                    break;
                case cantor::pb::ast::ENT_EXPR_LIST_ARG:
                    j["arg"] = ((const ExprListArg*)arg)->to_json();
                    break;
                default:
                    KCHECK(false) << "Unsupported arg type: " <<
                        pb::ast::ExprNodeType_Name(arg_type) << "int value: " << static_cast<int>(arg_type);
                    break;
            }
        } else {
            j["arg"] = nlohmann::ordered_json();
        }
        j["arg_type"] = pb::ast::ExprNodeType_Name(arg_type);
        return j;
    }

    void UdfArg::set_arg(MapArg * map) {
        arg_type = cantor::pb::ast::ENT_MAP_ARG;
        arg = map;
    }

    void UdfArg::set_arg(DictArg *dict) {
        arg_type = cantor::pb::ast::ENT_DICT_ARG;
        arg = dict;
    }

    void UdfArg::set_arg(ExprListArg* list) {
        arg_type = cantor::pb::ast::ENT_EXPR_LIST_ARG;
        arg = list;
    }

    nlohmann::ordered_json IndexLockOpt::to_json() const {
        auto j = DefiniteExpr::to_json_impl();
        if (lock_mode) {
            j["lock_mode"] = lock_mode->to_json();
        } else {
            j["lock_mode"] = nlohmann::ordered_json();
        }
        return j;
    }

    nlohmann::ordered_json AlgorithmOpt::to_json() const {
        auto j = DefiniteExpr::to_json_impl();
        if (algo) {
            j["algo"] = algo->to_json();
        } else {
            j["algo"] = nlohmann::ordered_json();
        }
        return j;
    }

    nlohmann::ordered_json VarAssign::to_json() const {
        auto j = DefiniteExpr::to_json_impl();
        j["name"] = key.to_string();
        if (value) {
            j["value"] = value->to_json();
        } else {
            j["value"] = nlohmann::ordered_json();
        }
        return j;
    }

    nlohmann::ordered_json NamespaceOption::to_json() const {
        auto j = DefiniteExpr::to_json_impl();
        j["type"] = cantor::pb::ast::NamespaceOptionType_Name(type);
        j["str_value"] = str_value.to_string();
        j["uint_value"] = uint_value;
        return j;
    }

    nlohmann::ordered_json TableName::to_json() const {
        auto j = DefiniteExpr::to_json_impl();
        j["ns_name"] = ns_name.to_string();
        j["db_name"] = db_name.to_string();
        j["table_name"] = table_name.to_string();
        return j;
    }
    nlohmann::ordered_json ColumnName::to_json() const {
        auto j = DefiniteExpr::to_json_impl();
        j["ns_name"] = ns_name.to_string();
        j["db_name"] = db_name.to_string();
        j["table_name"] = table_name.to_string();
        j["column_name"] = column_name.to_string();
        return j;
    }


    nlohmann::ordered_json TypeNameUri::to_json() const {
        auto j = DefiniteExpr::to_json_impl();
        j["ns_name"] = ns_name.to_string();
        j["db_name"] = db_name.to_string();
        j["table_name"] = table_name.to_string();
        j["type_name"] = type_name.to_string();
        return j;
    }

} // namespace cantor::parser
